package com.longtop.sp.mapdao.dialect.oracle;

import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.longtop.sp.mapdao.dialect.ConfigProperties;
import com.longtop.sp.mapdao.meta.TableMetaData;

/**
 * 获取表的字段和Map数据的交集，以便准备sql语句。
 * 
 * @author 赵斌 2003-6-23
 * @author 胡长城 2003-6-23
 * @version $Revision: 1.2 $
 */

public class FieldEntryList {
	private static Log log = LogFactory.getLog(FieldEntryList.class);

	private List lstFieldEntry = new ArrayList();
	private boolean haveLob = false;
	private boolean haveArray = false;

	public final static String TYPE_COMMON = "COMMON";
	public final static String TYPE_BLOB = "BLOB";
	public final static String TYPE_CLOB = "CLOB";
	public final static String TYPE_ARRAY = "ARRAY";

	/**
	 * 
	 * @param rsmtc
	 * @param rowData
	 * @throws SQLException
	 */
	public FieldEntryList(TableMetaData rsmtc, Map rowData) throws SQLException {
		for (int i = 1; i <= rsmtc.getColumnCount(); i++) {
			String fieldName = rsmtc.getName(i);
			Object o = rowData.get(fieldName);
			if (o == null) {
				continue;
				// if (rsmtc.isNullable(i)) {
				// continue;
				// }
				// else{
				// String msg = "字段不能为空，字段名：" + fieldName +
				// "，而您的传入的Map数据中没有此项的值。";
				// log.error(msg);
				// throw new SQLException(msg);
				// }
			}
			// 注销说明：取消不允许null字段的值匹配比较，这样，在更新时，不会造成非null子段的不更新操作检查异常

			int iType = rsmtc.getType(i);
			FieldEntry entry = new FieldEntry();
			entry.setFieldName(rsmtc.getName(i));
			entry.setFieldType(TYPE_COMMON);
			/** @todo 用空字符串表示 更新为 null ？ */
			switch (iType) {
			// 字符串
			case Types.CHAR:
			case Types.VARCHAR:
			case Types.LONGVARCHAR:
				// 进行长度判断
				String s = o.toString();
				// getBytes()Convert this String into bytes according to the
				// platform's default character encoding
				// 在pwin2000上，"中国"返回的长度是4，应该是ISO8859_1
				int len = s.getBytes().length;
				if (len > rsmtc.getPrecision(i)) {
					throw new SQLException("字段超长，字段名：" + fieldName + "  字段长度："
							+ rsmtc.getPrecision(i) + "    字符串长度：" + len);
				}
				entry.setFieldValue("'" + s.replace("'", "''") + "'");
				break;
			//数组
			case Types.ARRAY:
				this.haveArray = true;
				entry.setFieldValue(o.toString());
				entry.setFieldType(TYPE_ARRAY);
				break;
			// 整数
			case Types.BIGINT:
			case Types.BIT:
			case Types.INTEGER:
			case Types.SMALLINT:
			case Types.TINYINT:
				/** @todo 对于Int和浮点数，是否应进行校验？否则，可能insert 22.23 到int字段，自动就丢弃小数！ */
				if (o instanceof Integer || o instanceof Long) {
					entry.setFieldValue(o.toString());
				} else {
					String sint = o.toString();
					if (sint.trim().length() == 0) {
						sint = "null";
						/** @todo 替换为null，合适吗？ */
					} else {
						try {
							Integer.parseInt(sint);
						} catch (Exception ex) {
							throw new SQLException("无法保存整数，字段名：" + fieldName
									+ "，字段类型：" + rsmtc.getTypeName(i) + "，  值："
									+ o.toString());
						}
					}
					entry.setFieldValue(sint);
				}
				break;
			// 浮点数
			case Types.DECIMAL:
			case Types.DOUBLE:
			case Types.FLOAT:
			case Types.NUMERIC:/** @todo NUMERIC作为什么处理？ */
			case Types.REAL:
				if (o instanceof Float || o instanceof Double) {
					entry.setFieldValue(o.toString());
				} else {
					String sdouble = o.toString();
					if (sdouble.trim().length() == 0) {
						sdouble = "null";
					} else {
						try {
							Double.parseDouble(sdouble);
						} catch (Exception ex) {
							throw new SQLException("无法保存浮点数，字段名：" + fieldName
									+ "，字段类型：" + rsmtc.getTypeName(i) + "，  值："
									+ o.toString());
						}
					}
					entry.setFieldValue(sdouble);
				}
				break;
			case Types.DATE:
			case Types.TIME:
			case Types.TIMESTAMP:
				try {
					if (rsmtc.isNullable(i) && o.toString().length() == 0) {// huangyechun
						// added
					} else {
						String sdate = convert2DataStr(o);
						entry.setFieldValue(sdate);
					}
				} catch (SQLException ex) {
					throw new SQLException(ex.getMessage() + "字段名：" + fieldName
							+ "，字段值：" + o.toString());
				}
				break;
			case Types.BLOB:
				this.haveLob = true;
				entry.setFieldValue("EMPTY_BLOB()");
				entry.setFieldType(TYPE_BLOB);
				break;
			case Types.CLOB:
				this.haveLob = true;
				entry.setFieldValue("EMPTY_CLOB()");
				entry.setFieldType(TYPE_CLOB);
				break;
			default:
				String msg = "发现不能处理的数据库字段数据类型" + fieldName;
				log.error(msg);
				throw new SQLException(msg);
			} // end switch
			lstFieldEntry.add(entry);
		}
	}

	public boolean isHaveLob() {
		return haveLob;
	}
	public boolean isHaveArray() {
		return haveArray;
	}

	public String getFieldName(int i) {
		return ((FieldEntry) lstFieldEntry.get(i)).getFieldName();
	}

	public String getFieldValue(int i) {
		return ((FieldEntry) lstFieldEntry.get(i)).getFieldValue();
	}

	public String getFieldType(int i) {
		return ((FieldEntry) lstFieldEntry.get(i)).getFieldType();
	}

	public boolean isLob(int i) {
		String type = ((FieldEntry) lstFieldEntry.get(i)).getFieldType();
		return type.equals(TYPE_BLOB) || type.equals(TYPE_CLOB);
	}

	public boolean isBlob(int i) {
		return ((FieldEntry) lstFieldEntry.get(i)).getFieldType().equals(
				TYPE_BLOB);
	}

	public boolean isClob(int i) {
		return ((FieldEntry) lstFieldEntry.get(i)).getFieldType().equals(
				TYPE_CLOB);
	}

	public int getColumnCount() {
		return lstFieldEntry.size();
	}

	/**
	 * 将对象转换为该数据库中的字段串表示，如：对于Oracle转换为TO_Date('2003-5-1 15:24:59','YYYY-MM-DD
	 * HH24:MI:SS')
	 * 
	 * @param o
	 *            通常是java.util.Date对象，根据各个子类的具体实现不同，也可以是yyyy-MM-dd、yyyy/MM/dd形式的字符串或秒数。
	 * @return 该数据库中的字段串表示
	 * @throws SQLException
	 */
	public final String convert2DataStr(Object o) throws SQLException {
		java.util.Date date = convert2Date(o);
		Calendar cal = Calendar.getInstance();
		cal.setTime(date);
		// TO_Date('2001-1-17 11:10:14','YYYY-MM-DD HH24:MI:SS')
		// //java.util.Date中的month从0开始
		String sdate = null;
		if("mysql".equals(ConfigProperties.getValue())){
			sdate = "date_format('" + cal.get(Calendar.YEAR) + "-"
					+ (cal.get(Calendar.MONTH) + 1) + "-"
					+ cal.get(Calendar.DAY_OF_MONTH) + " "
					+ cal.get(Calendar.HOUR_OF_DAY) + ":"
					+ cal.get(Calendar.MINUTE) + ":" + cal.get(Calendar.SECOND)
					+ "', '%Y-%m-%d %H:%i:%s')";
			return sdate;
		}else if("postgresql".equals(ConfigProperties.getValue())){
			sdate = "to_timestamp('" + cal.get(Calendar.YEAR) + "-"
				+ (cal.get(Calendar.MONTH) + 1) + "-"
				+ cal.get(Calendar.DAY_OF_MONTH) + " "
				+ cal.get(Calendar.HOUR_OF_DAY) + ":"
				+ cal.get(Calendar.MINUTE) + ":" + cal.get(Calendar.SECOND)
				+ "','YYYY-MM-DD HH24:MI:SS')";
			return sdate;
		}else if("db2".equals(ConfigProperties.getValue())){
			return sdate;
		}else{
			sdate = "TO_Date('" + cal.get(Calendar.YEAR) + "-"
				+ (cal.get(Calendar.MONTH) + 1) + "-"
				+ cal.get(Calendar.DAY_OF_MONTH) + " "
				+ cal.get(Calendar.HOUR_OF_DAY) + ":"
				+ cal.get(Calendar.MINUTE) + ":" + cal.get(Calendar.SECOND)
				+ "','YYYY-MM-DD HH24:MI:SS')";
			return sdate;
		}
	}

	private java.util.Date convert2Date(Object o) throws SQLException {
		if (o instanceof java.util.Date) { // 注：java.sql.Date继承于java.util.Date
			return (java.util.Date) o;
		} else if (o instanceof Long || o instanceof Integer) {
			return new java.util.Date(((Long) o).longValue());
		} else if (o instanceof String) {
			String s = ((String) o).trim();
			if (s.length() == 0) {
				throw new SQLException("日期不能是空字符串，");
			}

			try {
				return new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
						.parse(s);
			} catch (Exception ex) {
			}

			try {
				return new java.text.SimpleDateFormat("yyyy/MM/dd HH:mm:ss")
						.parse(s);
			} catch (Exception ex) {
			}

			try {
				return new java.text.SimpleDateFormat("yyyy-MM-dd").parse(s);
			} catch (Exception ex) {
			}

			try {
				return new java.text.SimpleDateFormat("yyyy/MM/dd").parse(s);
			} catch (Exception ex) {
			}

			try {
				return new java.util.Date(Long.parseLong(s)); // 处理以字符串形式的秒数
			} catch (Exception ex) {
			}

			throw new SQLException("无法处理此字符串形式的日期数据类型，");
		} else {
			throw new SQLException("发现不能处理的日期数据类型，类型：" + o.getClass().getName()
					+ "，");
		}
	}

	class FieldEntry {
		private String fieldType;
		private String fieldName;
		private String fieldValue;

		public void setFieldType(String fieldType) {
			this.fieldType = fieldType;
		}

		public String getFieldType() {
			return fieldType;
		}

		public void setFieldName(String fieldName) {
			this.fieldName = fieldName;
		}

		public String getFieldName() {
			return fieldName;
		}

		public void setFieldValue(String fieldValue) {
			this.fieldValue = fieldValue;
		}

		public String getFieldValue() {
			return fieldValue;
		}

	}

}